
package io;

import info.Manuscript;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jdom2.DataConversionException;
import org.jdom2.Element;
import org.jdom2.JDOMException;

/**
 * This class provides access to Manuscript data for the peer review system.
 * 
 * @author Lun-Wei Chang
 * @author Aaron Chen
 * @author Christopher M. Augg
 * @author James Kantak
 * 
 * @version March 9, 2014
 * 
 */
public class ManuscriptAccess extends AbstractAccess {

  /** Name of the XML file. */
  private static final String XML_FILE = "Manuscript.xml";

  /** A constant for the manuscript id attribute in the XML file. */
  private static final String ATTRIBUTE_ID = "id";

  /** A constant for the manuscript id element in the XML file. */
  private static final String MANUSCRIPT_ID = "ManuscriptID";

  /** A constant for the title element in the XML file. */
  private static final String MANUSCRIPT_TITLE = "ManuscriptTitle";

  /** A constant for the authors element in the XML file. */
  private static final String MANUSCRIPT_AUTHORS = "Authors";

  /** A constant for the email element in the XML file. */
  private static final String MANUSCRIPT_EMAIL = "Email";

  /** A constant for the conference id element in the XML file. */
  private static final String MANUSCRIPT_CONFERENCE_ID = "Conference";

  /** A constant for the key words element in the XML file. */
  private static final String MANUSCRIPT_KEY_WORDS = "KeyWords";

  /** A constant for the conference category element in the XML file. */
  private static final String MANUSCRIPT_CATEGORY = "ConferenceCategory";

  /** A constant for the abstract element in the XML file. */
  private static final String MANUSCRIPT_ABSTRACT = "Abstract";

  /** A constant for the manuscript path element in the XML file. */
  private static final String MANUSCRIPT_PATH = "ManuscriptPath";

  /** A constant for the acceptance status element in the XML file. */
  private static final String ACCEPTANCE_STATUS = "AcceptanceStatus";

  /** A constant for the review IDs association element in the XML file. */
  private static final String MANUSCRIPT_REVIEW_IDS = "ReviewIDs";

  /** A constant for the review IDs association element in the XML file. */
  private static final String MANUSCRIPT_SUB_REV_ID = "RevID";

  /** A constant for the recommendation ID association element in the XML file. */
  private static final String MANUSCRIPT_REC_ID = "RecommendationID";

  /**
   * The constructor to instantiate the Manuscript objects.
   * 
   * @throws IOException If there is a problem establishing connectivity to the
   *           file.
   * @throws JDOMException If the file cannot be correctly parsed.
   */
  public ManuscriptAccess() throws IOException, JDOMException {
    super(XML_FILE);
  }

  /**
   * Returns a specific Manuscript.
   * 
   * @param the_id The identification number unique to each Manuscript.
   * @return Manuscript a single Manuscript.
   */
  public Manuscript getManuscript(final long the_id) {
    Manuscript m = null;
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren(MANUSCRIPT_ID);
    for (Element e : list) {
      try {
        if (e.getAttribute(ATTRIBUTE_ID).getLongValue() == the_id) {
          m = dataToManuscript(e);
          break;
        }
      } catch (final DataConversionException err) {
        err.printStackTrace();
      }
    }
    return m;
  }

  /**
   * Returns all the manuscripts.
   * 
   * @return A list of manuscripts.
   */
  public List<Manuscript> getAllManuscripts() {

    /** An array list to hold the manuscript's information. */
    final List<Manuscript> userdata = new ArrayList<Manuscript>();

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    for (Element e : list) {
      userdata.add(dataToManuscript(e));
    }
    return userdata;
  }

  /**
   * Returns all manuscripts of a user in a particular conference.
   * 
   * @param the_user The user id.
   * @param the_conf The conference id.
   * @param the_role The role id.
   * @return A list of manuscripts for the user.
   */
  public List<Long> getViewableManuscripts(final long the_user, final long the_conf,
                                           final long the_role) {
    UserHasConfHasManAccess ua = null;
    try {
      ua = new UserHasConfHasManAccess();
    } catch (final IOException e) {
      e.printStackTrace();
    } catch (final JDOMException e) {
      e.printStackTrace();
    }
    List<Long> paper = new ArrayList<Long>();
    paper = ua.getManuscriptID(the_user, the_conf, the_role);
    return paper;
  }

  /**
   * Returns the manuscripts that were submitted by the user id passed into the method.
   * 
   * @param the_user_id The id of the author of the manuscripts.
   * @return A list of manuscripts.
   */
  public List<Manuscript> getMySubmittedManuscripts(final long the_user_id) {

    /** An array list to hold the manuscript's information. */
    final List<Manuscript> userdata = new ArrayList<Manuscript>();

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();

    for (Element e : list) {
      final String text = e.getChildText("SubmittedID");
      final Long num = Long.parseLong(text);
      if (num == the_user_id) {
        userdata.add(dataToManuscript(e));
      }
    }
    return userdata;
  }
  
  /**
   * Returns the manuscripts that were assigned to the SPC with
   * the user id passed into the method.
   * 
   * @param the_spc_id The id of the author of the manuscripts.
   * @return A list of manuscripts.
   */
  public List<Manuscript> getManuscriptsAssignedToSPC(final long the_spc_id) {

    /** An array list to hold the manuscript's information. */
    final List<Manuscript> userdata = new ArrayList<Manuscript>();

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();

    for (Element e : list) {
      final String text = e.getChildText("AssignedSPC");
      final Long num = Long.parseLong(text);
      if (num == the_spc_id) {
        userdata.add(dataToManuscript(e));
      }
    }
    return userdata;
  }
  
  /**
   * Returns the SPC id assigned to the manuscript with
   * the manuscript id passed into the method.
   * 
   * @param the_paper The id of the the manuscript.
   * @return A spc id.
   */
  public String getSPCAssignedToManuscript(final long the_paper) {
  
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    String result = "empty";

    for (Element e : list) {
      final String text = e.getAttributeValue("id");
      final String text2 = e.getChildText("AssignedSPC");
     
      final Long num = Long.parseLong(text);
      
      if (num == the_paper) {
        return text2;
      }
    }
    return result;
  }
  
  

  /**
   * Writes a new manuscript entry into the database.
   * 
   * @param the_paper A new manuscript.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean createManuscript(final Manuscript the_paper) {
    boolean result = true;

    final Element rootnode = getDoc().getRootElement();
    final Element e = manuscriptToData(the_paper, false);
    rootnode.addContent(e);

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    result = writeToDatabase();
    if (!result) {
      e.detach();
    }

    return result;
  }

  /**
   * Writes over a pre-existing manuscript in the database.
   * 
   * @param the_paper The Manuscript data to write into the database.
   * @return true if the manuscript to edit was found and overwritten; false
   *         otherwise.
   */
  public boolean editManuscript(final Manuscript the_paper) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element old_element = null;
    final Element new_element = manuscriptToData(the_paper, true);
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_paper.getID()) {
        old_element = e.detach();
        rootnode.addContent(new_element);
        result = true;
        break;
      }
    }

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(old_element);
        new_element.detach();
      }
    }

    return result;
  }

  
  /** Delete a manuscript.
   * @param the_id the id of the manuscript. 
   * @return Returns true if the manuscript is deleted else returns false. 
   */
  public boolean deleteManuscript(final long the_id) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element element = null;
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_id) {
        element = e.detach();
        result = true;
        break;
      }
    }
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(element);
      }
    }

    return result;
  }

  /**
   * Translates the XML element data into a Manuscript object.
   * 
   * @param the_element The element that contains Manuscript data.
   * @return a Manuscript object.
   */
  private Manuscript dataToManuscript(final Element the_element) {
    final long id = Long.parseLong(the_element.getAttributeValue(ATTRIBUTE_ID));
    String str = the_element.getChildText(MANUSCRIPT_CONFERENCE_ID);
    long conference_id = 0;
    if (isNonemptyString(str)) {
      conference_id = Long.parseLong(str);
    }

    final List<String> authors = new ArrayList<String>();
    authors.add(the_element.getChildText(MANUSCRIPT_AUTHORS));
    final List<String> key_words = new ArrayList<String>();
    key_words.add(the_element.getChildText(MANUSCRIPT_KEY_WORDS));
    final List<String> categories = new ArrayList<String>();
    categories.add(the_element.getChildText(MANUSCRIPT_CATEGORY));

    str = the_element.getChildText(ACCEPTANCE_STATUS);
    int status = 0;
    if (str != null && !str.isEmpty()) {
      status = Integer.parseInt(str);
    }

    final Element eq = the_element.getChild(MANUSCRIPT_REVIEW_IDS);
    final List<Element> rev_nodes = eq.getChildren();
    final Iterator<Element> itr = rev_nodes.iterator();
    final List<Long> review_ids = new ArrayList<Long>();
    for (int i = 0; i < rev_nodes.size(); i++) {
      review_ids.add(Long.parseLong(itr.next().getText()));
    }

    final long recommendation_id = Long.parseLong(the_element.getChildText(MANUSCRIPT_REC_ID));

    final Manuscript m = new Manuscript(id,
                                        the_element.getChildText(MANUSCRIPT_TITLE),
                                        authors,
                                        the_element.getChildText(MANUSCRIPT_EMAIL),
                                        conference_id,
                                        key_words,
                                        categories,
                                        the_element.getChildText(MANUSCRIPT_ABSTRACT),
                                        the_element.getChildText(MANUSCRIPT_PATH),
                                        status,
                                        review_ids,
                                        recommendation_id);
    return m;
  }

  /**
   * Translates the Manuscript object into XML element data.
   * 
   * @param the_paper The Manuscript object to translate.
   * @param the_edit True if this is an edit operation; false if it is a new
   *          entry.
   * @return an XML element node.
   */
  private Element manuscriptToData(final Manuscript the_paper, final boolean the_edit) {
    final Element usernode = new Element(MANUSCRIPT_ID);

    if (!the_edit) {
      final long id = getNextID();
      usernode.setAttribute(ATTRIBUTE_ID, Long.toString(id));
      the_paper.setID(id);
    } else {
      usernode.setAttribute(ATTRIBUTE_ID, Long.toString(the_paper.getID()));
    }

    usernode.addContent(
        new Element(MANUSCRIPT_AUTHORS).setText(the_paper.getSubmittingAuthor()));
    usernode.addContent(new Element(MANUSCRIPT_TITLE).setText(the_paper.getTitle()));
    usernode.addContent(
        new Element(ACCEPTANCE_STATUS).setText("" + the_paper.getDecisionStatus()));
    usernode.addContent(
        new Element(MANUSCRIPT_KEY_WORDS).setText(the_paper.getKeyWords().get(0)));
    usernode.addContent(
        new Element(MANUSCRIPT_CATEGORY).setText(the_paper.getCategories().get(0)));
    usernode.addContent(new Element(MANUSCRIPT_EMAIL).setText(the_paper.getEmail()));
    usernode.addContent(
        new Element(MANUSCRIPT_CONFERENCE_ID).setText("" + the_paper.getConferenceID()));
    usernode.addContent(new Element(MANUSCRIPT_ABSTRACT).setText(the_paper.getAbstract()));
    usernode.addContent(new Element(MANUSCRIPT_PATH).setText(the_paper.getDirectoryPath()));

    final Element review_node = new Element(MANUSCRIPT_REVIEW_IDS);
    final List<Long> review_ids = the_paper.getReviewIDs();
    for (int i = 0; i < review_ids.size(); i++) {
      final String str = Long.toString(review_ids.get(i));
      review_node.addContent(new Element(MANUSCRIPT_SUB_REV_ID).setText(str));
    }
    usernode.addContent(review_node);

    final String str = Long.toString(the_paper.getRecommendationID());
    usernode.addContent(new Element(MANUSCRIPT_REC_ID).setText(str));

    return usernode;

  }

}
